package com.czmh.etc.onlinepubsys.interceptors;

import com.czmh.etc.onlinepubsys.constants.RedisKey;
import com.czmh.etc.onlinepubsys.utils.RedisUtil;
import com.czmh.etc.onlinepubsys.utils.RequestUtil;
import com.google.common.collect.ImmutableList;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * @描述：JwtInterceptor
 * @author： Seven.
 * @创建日期：2019年06月25日
 * @版本：1.0.0
 */
public class JwtInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        Map<String, String> headers = RequestUtil.getHeaders(request);
        String access_token = headers.get("Authorization");
        if (StringUtils.isEmpty(access_token)) {
            access_token = headers.get("token");
            if (StringUtils.isEmpty(access_token)) {
                response.setStatus(401);
                return false;
            }
        }
        if (null == redisUtil) {
            throw new Exception("redisUtil为空");
        }
        if (null == redisUtil.get(RedisKey.APP_TOKEN + access_token)) {
            return false;
        } else {
            String authInfo = redisUtil.get(RedisKey.APP_TOKEN + access_token).toString();
            if (StringUtils.isEmpty(authInfo)) {
                return false;
            } else {

                //判断是否开启限流操作
                Object limitEnable = redisUtil.get("limit-api:config:limitEnable");
                if (limitEnable != null && (limitEnable.toString()).equalsIgnoreCase("1")) {
                    String key;
                    int limitPeriod = 0;
                    int limitCount = 0;
                    if (limitPeriod == 0 && limitCount == 0) {
                        //以下可以通过redis获取配置的周期次数参数
                        Object limitPeriodSerializable = redisUtil.get("limit-api:config:userLimitPeriod");
                        Object limitCountSerializable = redisUtil.get("limit-api:config:userLimitCount");
                        if (limitPeriodSerializable != null && limitCountSerializable != null) {
                            limitPeriod = ((Integer) (limitPeriodSerializable)).intValue();
                            limitCount = ((Integer) (limitCountSerializable)).intValue();
                        } else {
                            limitPeriod = 1;
                            limitCount = 2;
                        }
                    }
                    key = request.getMethod().toString();
                    ImmutableList<String> keys = ImmutableList.of(StringUtils.join("limit-api:user:" + authInfo + ":", key));
                    try {
                        String luaScript = buildLuaScript();
                        RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);
                        Number count = redisUtil.execute(redisScript, keys, limitCount, limitPeriod);
                        //logger.info("Access try count is {} for name={} and key = {}", count, name, key);
                        if (count != null && count.intValue() <= limitCount) {
                            return true;
                        } else {
                            throw new RuntimeException("You request too fast!");
                        }
                    } catch (Throwable e) {
                        if (e instanceof RuntimeException) {
                            throw new RuntimeException(e.getLocalizedMessage());
                        }
                        throw new RuntimeException("server exception");
                    }
                }
                return true;
            }
        }
    }

    /**
     * 限流 脚本
     *
     * @return lua脚本
     */
    public String buildLuaScript() {
        StringBuilder lua = new StringBuilder();
        lua.append("local c");
        lua.append("\nc = redis.call('get',KEYS[1])");
        // 调用不超过最大值，则直接返回
        lua.append("\nif c and tonumber(c) > tonumber(ARGV[1]) then");
        lua.append("\nreturn c;");
        lua.append("\nend");
        // 执行计算器自加
        lua.append("\nc = redis.call('incr',KEYS[1])");
        lua.append("\nif tonumber(c) == 1 then");
        // 从第一次调用开始限流，设置对应键值的过期
        lua.append("\nredis.call('expire',KEYS[1],ARGV[2])");
        lua.append("\nend");
        lua.append("\nreturn c;");
        return lua.toString();
    }

    private static final String UNKNOWN = "unknown";

    public String getIpAddress() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}